package com.acmen.easyapi.concurrent;

import com.acmen.easyapi.model.*;
import com.acmen.easyapi.model.*;
import com.acmen.easyapi.util.MultiValueMapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.springframework.http.*;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Callable;

/**
 * 组合测试请求线程任务
 * @author acmen
 * @date 2022/07/20
 */
public class TestCallable implements Callable<CallResult> {
    private static RestTemplate restTemplate = new RestTemplate();
    private Map<String, String> headers;
    private String basePath;
    private Api apiDetail;
    private String testName;
    private RequestData requestData;
    /**
     * 预期响应
     */
    private Response expectResponse;
    private boolean completed = false;

    public TestCallable(String testName,String basePath, Api apiDetail,Map<String, String> headers, RequestData requestData,Response expectResponse) {
        this.testName = testName;
        this.basePath = basePath;
        this.apiDetail = apiDetail;
        this.headers = headers;
        this.requestData = requestData;
        this.expectResponse = expectResponse;
    }
    @Override
    public CallResult call() {
        CallResult callResult = buidCallResult();
        String url = basePath+apiDetail.getPath();
        HttpMethod httpMethod = HttpMethod.valueOf(apiDetail.getRequestMethod());
        MultiValueMap<String, String> httpHeaders = MultiValueMapUtil.toMultiValueMap(headers);
        httpHeaders.addAll(MultiValueMapUtil.toMultiValueMap(requestData.getHeaders()));
        Object formData = null;
        if(httpMethod.equals(HttpMethod.GET)||httpMethod.equals(HttpMethod.DELETE)){
            if(requestData.getParams()!=null) {
                url = UriComponentsBuilder.fromHttpUrl(url).queryParams(MultiValueMapUtil.toMultiValueMap(requestData.getParams())).toUriString();
            }
        }else if(httpMethod.equals(HttpMethod.POST)||httpMethod.equals(HttpMethod.PUT)||httpMethod.equals(HttpMethod.PATCH)){
            if(apiDetail.getReqParamNode().getSubNodes().stream().anyMatch(e->e.getName().equals(ReqParamNode.NODE_BODY))){
                formData = requestData.getBody()!=null?requestData.getBody():new JSONObject();
                httpHeaders.set(HttpHeaders.CONTENT_TYPE ,MediaType.APPLICATION_JSON.toString());
                if(requestData.getParams()!=null) {
                    url = UriComponentsBuilder.fromHttpUrl(url).queryParams(MultiValueMapUtil.toMultiValueMap(requestData.getParams())).toUriString();
                }
            }else{
                httpHeaders.set(HttpHeaders.CONTENT_TYPE ,MediaType.APPLICATION_FORM_URLENCODED.toString());
                if(requestData.getParams()!=null) {
                    formData = MultiValueMapUtil.toMultiValueMap(requestData.getParams());
                }
            }
        }
        HttpEntity requestEntity = new HttpEntity(formData, httpHeaders);
        long beginTime = System.currentTimeMillis();
        try {
            ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(url, httpMethod, requestEntity, JSONObject.class);
            JSONObject newObj = new JSONObject();
            newObj.put("code",responseEntity.getBody().get("code"));
            newObj.put("status",responseEntity.getBody().get("status"));
            newObj.put("message",responseEntity.getBody().get("message"));
            callResult.setResult(newObj.toJSONString());
            callResult.setReceiveSize(responseEntity.getBody().toJSONString().getBytes().length);
            callResult.setResponseSuccess(true);
            boolean success = Objects.equals(responseEntity.getBody().get("code"),expectResponse.getCode())
                    &&Objects.equals(responseEntity.getBody().getBoolean("success"),expectResponse.isSuccess());
            callResult.setBusinessSuccess(success);
            return callResult;
        }catch (Exception e){
            e.printStackTrace();
            callResult.setResponseSuccess(false);
            callResult.setBusinessSuccess(false);
            callResult.setResult(e.getMessage());
            callResult.setReceiveSize(0);
            return callResult;
        }finally {
            long endTime = System.currentTimeMillis();
            callResult.setCost(endTime-beginTime);
            completed = true;
        }
    }

    private CallResult buidCallResult() {
        CallResult callResult = new CallResult();
        callResult.setId(UUID.randomUUID().toString().replace("-", ""));
        callResult.setTestName(testName);
        callResult.setExpectResponse(JSON.toJSONString(expectResponse.setData(null)));
        callResult.setApiId(apiDetail.getId());
        callResult.setBeginTime(LocalDateTime.now());
        long size = 0;
        if(requestData.getParams()!=null) {
            callResult.setParams(requestData.getParams().toJSONString());
            size += callResult.getParams().getBytes().length;
        }
        if(requestData.getBody()!=null) {
            callResult.setBody(requestData.getBody().toJSONString());
            size += callResult.getBody().getBytes().length;
        }
        if(requestData.getHeaders()!=null){
            callResult.setHeaders(requestData.getHeaders().toJSONString());
            size += callResult.getHeaders().getBytes().length;
        }
        callResult.setSendSize(size);
        return callResult;
    }


    public boolean isCompleted() {
        return completed;
    }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }
}
